package ac.github.oa.internal.core.ui

import ac.github.oa.api.ItemAPI.setDurability
import ac.github.oa.api.NBT
import ac.github.oa.api.compat.PlayerPointsHook.PlayerPointsAPI
import ac.github.oa.api.event.item.ItemRepairEvent
import ac.github.oa.internal.core.item.Item
import ac.github.oa.internal.core.item.ItemInstance
import ac.github.oa.internal.core.item.ItemPlant
import ac.github.oa.internal.core.ui.Repair.allInfo
import ac.github.oa.internal.core.ui.Repair.baffle
import ac.github.oa.internal.core.ui.Repair.confirm
import ac.github.oa.internal.core.ui.Repair.confirmIcon
import ac.github.oa.internal.core.ui.Repair.protect
import ac.github.oa.internal.core.ui.Repair.repair
import ac.github.oa.internal.core.ui.Repair.root
import ac.github.oa.internal.core.ui.Repair.save
import me.clip.placeholderapi.PlaceholderAPI
import org.bukkit.entity.Player
import org.bukkit.event.inventory.ClickType
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemFlag
import org.bukkit.inventory.ItemStack
import taboolib.common.platform.function.submitAsync
import taboolib.library.xseries.getItemStack
import taboolib.module.chat.colored
import taboolib.module.nms.getItemTag
import taboolib.module.ui.buildMenu
import taboolib.module.ui.type.Basic
import taboolib.platform.compat.getBalance
import taboolib.platform.compat.withdrawBalance
import taboolib.platform.util.buildItem
import taboolib.platform.util.giveItem
import taboolib.platform.util.isAir
import taboolib.platform.util.sendLang

class RepairUI(val viewer: Player) {

    var opened: Inventory? = null
    val repairInfo = RepairInfo()

    fun build() : Inventory {
        return buildMenu<Basic>(Repair.title.colored()) {
            rows(Repair.rows)
            handLocked(true)
            root.getKeys(false).filter { it.startsWith("icon-") }.forEach {
                val itemStack = buildItem(root.getItemStack(it)!!) {
                    flags += ItemFlag.values()
                }
                root.getIntegerList("${it}.slots").forEach { slot ->
                    set(slot, itemStack) {
                        isCancelled = true
                    }
                }
            }
            set(confirm, makeItem(confirmIcon)) {
                repaired()
            }
            onBuild { _, inventory ->
                opened = inventory
                allInfo[viewer] = repairInfo
            }
            onClick { event ->
                event.clickEventOrNull()?.apply {
                    if (click == ClickType.LEFT || click == ClickType.RIGHT) {
                        if (baffle.hasNext(viewer.name)) {
                            if (rawSlot in protect) {
                                if (cursor != null && !cursor.isAir) {
                                    val type = ItemInstance.get(cursor!!)?.item?.repair?.type
                                    if (type != "protect") {
                                        event.isCancelled = true
                                    }
                                }
                            }
                            if (rawSlot == repair) {
                                if (cursor != null && !cursor.isAir) {
                                    val itemInstance = ItemInstance.get(cursor!!)
                                    if (itemInstance == null) {
                                        event.isCancelled = true
                                        return@onClick
                                    } else {
                                        val type = itemInstance.item.repair.type
                                        if (type != "normal") {
                                            event.isCancelled = true
                                        }
                                    }
                                }
                            }
                            if (rawSlot in save) {
                                if (cursor != null && !cursor.isAir) {
                                    val type = ItemInstance.get(cursor!!)?.item?.repair?.type
                                    if (type != "save") {
                                        event.isCancelled = true
                                    }
                                }
                            }
                            allInfo[viewer] = repairInfo
                        } else {
                            event.isCancelled = true
                            viewer.sendLang("click-too-fast")
                        }
                        submitAsync {
                            refresh()
                        }
                    } else {
                        event.isCancelled = true
                    }
                }
            }
            onClose {
                fun back(slot: Int) {
                    val itemStack = it.inventory.getItem(slot)
                    if (itemStack.isAir()) return
                    viewer.giveItem(itemStack)
                    it.inventory.setItem(slot, null)
                }
                back(repair)
                protect.forEach { slot ->
                    back(slot)
                }
                save.forEach { slot ->
                    back(slot)
                }
                allInfo.remove(viewer)
            }
        }
    }

    private fun refresh() {
        val item = opened?.getItem(repair)
        val newItem = makeItem(confirmIcon, item)
        opened?.setItem(confirm, newItem)
    }

    private fun makeItem(item: ItemStack, repairItem: ItemStack? = null): ItemStack {
        val itemItem = ItemPlant.parseItem(repairItem)
        if (itemItem == null || repairItem == null) {
            return buildItem(item) {
                val papi = PlaceholderAPI.setPlaceholders(viewer, lore)
                lore.clear()
                lore.addAll(papi)
                colored()
            }
        } else {
            val repair = itemItem.repair
            return buildItem(item) {
                repairInfo.money = repair.money
                repairInfo.points = repair.points
                repairInfo.save = getSavePercent(itemItem)
                repairInfo.protect = getProtectPercent(itemItem)
                repairInfo.newDurability = getNewDurability(repairItem, getProtectPercent(itemItem))
                repairInfo.allMoney = getMoney(itemItem, getDelta(repairItem))
                repairInfo.allPoints = getPoints(itemItem, getDelta(repairItem))
                repairInfo.durability = getDelta(repairItem)
                allInfo[viewer] = repairInfo
                val papi = PlaceholderAPI.setPlaceholders(viewer, lore)
                lore.clear()
                lore.addAll(papi)
                colored()
            }
        }
    }

    private fun repaired() {
        val item = opened?.getItem(repair) ?: run {
            viewer.sendLang("repair-nothing")
            return
        }
        val repairItem = ItemPlant.parseItem(item)!!
        if (item.isAir) return
        val delta = getDelta(item)
        val points = getPoints(repairItem, delta)
        val money = getMoney(repairItem, delta)
        val event = ItemRepairEvent.Pre(viewer, item, money, points, getNewDurability(item, getProtectPercent(repairItem)), item.getItemTag()[NBT.DURABILITY.key]!!.asInt(), item.getItemTag()[NBT.MAX_DURABILITY.key]!!.asInt())
        event.call()
        if (money > viewer.getBalance()) {
            viewer.sendLang("repair-money-not-enough", money)
            return
        } else {
            viewer.withdrawBalance(money)
        }
        if (PlayerPointsAPI != null) {
            if (points > PlayerPointsAPI!!.look(viewer.uniqueId)) {
                viewer.sendLang("repair-points-not-enough", points)
                return
            } else {
                PlayerPointsAPI!!.take(viewer.uniqueId, points)
            }
        }
        save.forEach {
            opened?.getItem(it)?.let { item ->
                if (!item.isAir) {
                    item.amount -= 1
                }
            }
        }
        protect.forEach {
            opened?.getItem(it)?.let { item ->
                if (!item.isAir) {
                    item.amount -= 1
                }
            }
        }
        val newDurability = getNewDurability(item,getProtectPercent(repairItem))
        if (newDurability == 0) {
            opened!!.setItem(repair, null)
            viewer.sendLang("repair-lowest")
            ItemRepairEvent.Lowest(viewer, item).call()
            return
        }
        val randomNum = (1..100).random()
        if (randomNum > getSavePercent(repairItem)*100) {
            opened!!.setItem(repair, null)
            ItemRepairEvent.Failure(viewer, item, money, points).call()
            viewer.sendLang("repair-fail")
            return
        }
        setDurability(item, newDurability, newDurability)
        ItemRepairEvent.Post(viewer, item, money, points, event.new, event.old, event.oldmax).call()
        viewer.sendLang("repair-success", newDurability)
        submitAsync {
            refresh()
        }
    }

    fun open() {
        viewer.openInventory(build())
    }

    private fun getProtectPercent(repairItem: Item): Double {
        val repair = repairItem.repair
        val start = repair.protect
        return getPercent(start, protect, 1)
    }

    private fun getSavePercent(repairItem: Item): Double {
        val repair = repairItem.repair
        val start = repair.save
        return getPercent(start, save, 2)
    }

    private fun getPercent(start: Double, list: List<Int>, type: Int): Double {
        var start1 = start
        list.forEach {
            val item = opened?.getItem(it) ?: return@forEach
            val repair = ItemInstance.get(item)!!.item.repair
            when (type) {
                1->{ start1 += repair.protect }
                2->{ start1 += repair.save }
            }
        }
        return if (start1 > 1) {
            1.0
        } else if (start1 < 0) {
            0.0
        } else {
            start1
        }
    }

    private fun getNewDurability(repairItem: ItemStack, protectPercent: Double): Int {
        val itemTag = repairItem.getItemTag()
        val maxDurability = itemTag[NBT.MAX_DURABILITY.key]!!.asInt()
        return (maxDurability * protectPercent).toInt()
    }

    private fun getMoney(repairItem: Item, delta: Int): Double {
        val repair = repairItem.repair
        return repair.money * delta
    }

    private fun getPoints(repairItem: Item, delta: Int): Int {
        val repair = repairItem.repair
        return repair.points * delta

    }

    private fun getDelta(repairItem: ItemStack): Int {
        val itemTag = repairItem.getItemTag()
        val item = ItemPlant.parseItem(repairItem)!!
        val delta = getNewDurability(repairItem, getProtectPercent(item)) - itemTag[NBT.DURABILITY.key]!!.asInt()
        return if (delta >= 0) {
            delta
        } else {
            0
        }
    }

}